package sketch.specs;

import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.Assignment;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
import org.eclipse.jdt.internal.core.dom.NaiveASTFlattener;

import randoop.util.Reflection;
import sketch.ast.ASTUtils;
import sketch.ounit.Values;
import sketch.util.Checker;
import treemap.TreeMap;

//XXX this class should be somehow duplicated with RandomFuzzExtractor
public class RandomValueVisitor extends NaiveASTFlattener {

	//XXX did not consider just calling randoop without assigning a value
	
	public final Map<MethodInvocation, SimpleName> randomValues =
		new LinkedHashMap<MethodInvocation, SimpleName>();
	
	//public final Set<String> randomCalls = new LinkedHashSet<String>();
	public final Map<String,Set<String>> random_call_map = new LinkedHashMap<String, Set<String>>();
	
	public RandomValueVisitor() {
//		randomCalls.add("randoop");
//		randomCalls.add("random");
		/** initilize the map */
		addKeyValuePair("Values", "randomCreate");
		addKeyValuePair("sketch.ounit.Values", "randomCreate");
		addKeyValuePair("Values", "randomMutate");
		addKeyValuePair("sketch.ounit.Values", "randomMutate");
		addKeyValuePair("Values", "random");
		addKeyValuePair("sketch.ounit.Values", "random");
		addKeyValuePair("Values", "randoop");
		addKeyValuePair("sketch.ounit.Values", "randoop");
		
		//the old interfaces
		addKeyValuePair("Values", "randomFuzz");
		addKeyValuePair("sketch.ounit.Values", "randomFuzz");
	}
	
	private void addKeyValuePair(String key, String value) {
		if(!random_call_map.containsKey(key)) {
			random_call_map.put(key, new LinkedHashSet<String>());
		}
		random_call_map.get(key).add(value);
	}
	
	public Map<MethodInvocation, SimpleName> getRandomValues() {
		return this.randomValues;
	}

	public static List<Class<?>> getClasses(MethodInvocation method) {
		List<Class<?>> clazzlist = new LinkedList<Class<?>>();
		List<Expression> expressions = method.arguments();
		for(Expression expr : expressions) {
			String clazz = expr.toString();
			if(clazz.endsWith(".class")) {
				clazzlist.add(Reflection.classForName(clazz.substring(0, clazz.length() - ".class".length())));
			}
		}
		return clazzlist;
	}
	
	@Override
	public boolean visit(MethodInvocation method) {
		String methodName = method.getName().getIdentifier();
		String expressionName = method.getExpression().toString();
		
		if(this.random_call_map.containsKey(expressionName)) {
			if(this.random_call_map.get(expressionName).contains(methodName)) {
				ASTNode parent = method.getParent();
				Checker.checkTrue(parent instanceof Assignment, "You may only use random values in an assignment statement.");
				Assignment assignment = (Assignment)parent;
				Expression left = assignment.getLeftHandSide();
				//System.out.println(left + ", type: " + left.getClass());
				Checker.checkTrue(left instanceof SimpleName, "The left part should be a simple name.");
				SimpleName assignedName = (SimpleName)left;
				randomValues.put(ASTUtils.deepClone(method), assignedName);
			}
		}
		return super.visit(method);
	}
}